from zope import interface
import os, sys

APPLICATION_INSTALL_DIR = os.environ.get('SIMPLEWATCHER_INSTALL')
if not APPLICATION_INSTALL_DIR:
    raise EnvironmentError("must set environment variable SIMPLEWATCHER_INSTALL")
sys.path.append('%s/lib' % APPLICATION_INSTALL_DIR)


class ISchemaConstraint(interface.Interface):
    """
    schema constraint object that attaches to all simplewatcherdataobjects, 
    defining constraints for valid_children, embedded_resources, only_child, etc
    """
    valid_children = interface.Attribute('valid_children', \
        """
        Valid children should be a simple array of interfaces
        that are allowed to be attached to the data object
        """)
    embedded_resources = interface.Attribute('embedded_resources', \
        """
        Resources that cannot exist on their own, but must be
        embedded into another resource in order to be active
        """)
    only_child = interface.Attribute('only_child', \
        """
        an array of interfaces that can have only one instance attached
        to the parent
        """)
    __parent__ = interface.Attribute('__parent__', \
        """
        actual instance of parent class to which schema constraint is attached
        """)
    
    def validate_children():
        """runs through all children and verifies that they implement an allowed
           interface from valid children or embedded resources,
           and checks that only one of each only child interfaces is provided"""
    
    

class SchemaConstraint(object):
    interface.implements(ISchemaConstraint)    
    """implementation of schema constraint"""
    def __init__(self, parent):
        self.parent = parent
    
    @property
    def valid_children(self):
        return self.parent._valid_children
    
    @property
    def embedded_resources(self):
        return self.parent._embedded_resources
    
    @property
    def only_child(self):
        return self.parent._only_child
        
    
    @property
    def children(self):
        return self.parent._children


    def check_all_only_child(self):
        only_children = []
        invalid_children = []
        for child in self.children:
            for oc in self.only_child:
                if oc.providedBy(child):
                    if oc in only_children:
                        invalid_children.append((child, "More than one child provides: %s" % oc))
                    only_children.append(oc)
        return invalid_children


    def check_unique_only_child(self, child):
        check_interface = None
        for oc in self.only_child:
            if oc.providedBy(child):
                check_interface = oc
        if not check_interface:
            return True
        for c in self.children:
            if check_interface.providedBy(c):
                return False
        return True


    def validate_children(self):
        if not len(self.children) > 0:
            return True
        invalid_children = []
        for child in self.children:
            valid, message = self.validate_child(child)
            if not valid:
                invalid_children.append((child, message))
        invalid_children.extend(self.check_all_only_child())
        if len(invalid_children) > 0:
            raise TypeError("Invalid children! %s" % [(child.__class__, message) for (child, message) in invalid_children])
        return True
    


    def validate_child(self, child, checkOnly=False):
        child_validated = (False, "Child Provided no valid interface")
        for vint in self.valid_children:
            if vint.providedBy(child):
                child_validated = (True, "Valid Interface Provided")
        for er in self.embedded_resources:
            if er.providedBy(child):
                child_validated = (True, "Child is Embedded Resource")
        if checkOnly:
            if not self.check_unique_only_child(child):
                child_validated = (False, "More than one child provides this Interface")
        return child_validated